package com.smallframework.mvc;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.util.*;
import java.util.concurrent.CopyOnWriteArraySet;

/**
 * Created by tan on 2017/7/3.
 */
public class IOCContainer {
	private ClassHelper classHelper;
	private Map<Class<?>, Object> classInstanceMap = new HashMap<>();

	public Object getBean(Class<?> clazz){
		return classInstanceMap.get(clazz);
	}


	//暂时不支持 循环依赖
	public void createInstanceAndInjectField() throws Exception {
		List<Class<? extends Annotation>> annotationClasses = new ArrayList<>();
		annotationClasses.add(Component.class);
		annotationClasses.add(Controller.class);
		List<Class<?>> classes = classHelper.getClassesByAnnotations(annotationClasses);


		//使用copyonwrite 避免 在遍历迭代器过程中 删除元素，抛出异常。
		Set<Class<?>> classSet = new CopyOnWriteArraySet<>(classes);
		for (Class<?> clazz : classSet) {
			Object classInstance = clazz.newInstance();
			classInstanceMap.put(clazz, classInstance);
			//移除clazz
			classSet.remove(clazz);

			injectField(clazz, classInstance, classSet);
		}
	}

	private void injectField(Class<?> clazz, Object clazzInstance, Set<Class<?>> classSet) throws Exception {
		Field[] fields = clazz.getDeclaredFields();
		for (Field field : fields) {
			Inject inject = field.getDeclaredAnnotation(Inject.class);
			if (inject != null) {
				Class<?> fieldClazz = field.getType();
				Object fieldInstance = classInstanceMap.get(fieldClazz);
				if (fieldInstance != null) {
					field.setAccessible(true);
					field.set(clazzInstance, fieldInstance);
				} else {
					//判断fieldClazz是否在 classes中
					boolean exist = classSet.contains(fieldClazz);
					if (exist) {
						//存在  创建bean
						fieldInstance = fieldClazz.newInstance();
						classInstanceMap.put(fieldClazz, fieldInstance);
						classSet.remove(fieldClazz);

						//解决依赖
						injectField(fieldClazz, fieldInstance, classSet);

					} else {
						//对于循环依赖的 也会抛出此
						throw new Exception("compnent not exist");
					}
				}
			}
		}
	}

	public ClassHelper getClassHelper() {
		return classHelper;
	}

	public void setClassHelper(ClassHelper classHelper) {
		this.classHelper = classHelper;
	}
}
